#pragma once

#include <iostream>
#include <functional>
using namespace std;

template<class T>
class Shared_ptr
{
public:
	explicit Shared_ptr(T* ptr = nullptr)
		:_ptr(ptr)
		, _pcount(new int(1))
	{}

	template<class D>
	explicit Shared_ptr(T* ptr, D _del)
		:_ptr(ptr)
		, _pcount(new int(1))
		, _del(del)
	{}

	Shared_ptr(const Shared_ptr<T>& sp)
		:_ptr(sp._ptr)
		, _pcount(sp._pcount)
		, _del(sp._del)
	{
		(*_pcount)++;
	}

	Shared_ptr<T>& operator=(const Shared_ptr<T>& sp)
	{
		if (sp != *this)
		{
			release();

			_ptr = sp._ptr;
			_pcount = sp._pcount;
			(*_pcount)++;
		}
		return *this;
	}

	~Shared_ptr()
	{
		release();
	}

	void release()
	{
		(*_pcount)--;
		if (*_pcount == 0)
		{
			_del(_ptr);
			delete _pcount;
		}
	}

	T* get()
	{
		return _ptr;
	}

	int use_count()
	{
		return *_pcount;
	}

	T& operator*()
	{
		return *_ptr;
	}

	T* operator->()
	{
		return _ptr;
	}

	T& operator[](int i)
	{
		return _ptr[i];
	}

	operator bool()
	{
		return _ptr != nullptr;
	}
private:
	T* _ptr;
	int* _pcount;
	function<void(T*)> _del = [](T* ptr) {delete ptr; };
};